package com.yy.device.boot;

import com.alibaba.fastjson.JSON;
import com.yy.device.boot.handler.ProtoDecoder;
import com.yy.device.boot.handler.ProtoEncoder;
import com.yy.device.boot.handler.ServerDecoder;
import com.yy.device.boot.handler.ServerHandler;
import com.yy.device.boot.handler.TimeOutHandler;
import com.yy.device.boot.protocol.TaskMessage;
import com.yy.device.config.ExecutorServiceConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * 网络服务器
 *
 * @author sunzhiqiang
 * @date 2021/12/13 11:06:36
 */
@Slf4j
@Data
public class DeviceServer {

    private int port;
    private int deviceServerBossSize;
    private int deviceServerWokerSize;
    private int soBlcklog;

    public DeviceServer(int port, int deviceServerBossSize, int deviceServerWokerSize, int soBlcklog) {
        this.port = port;
        this.deviceServerBossSize = deviceServerBossSize;
        this.deviceServerWokerSize = deviceServerWokerSize;
        this.soBlcklog = soBlcklog;
        this.connect(deviceServerBossSize, deviceServerWokerSize, soBlcklog);
    }

    public void connect(int deviceServerBossSize, int deviceServerWokerSize, Integer soBlcklog) {
        ExecutorServiceConfig.executorService.execute(() -> {
            final NioEventLoopGroup bossGroup = new NioEventLoopGroup(deviceServerBossSize);
            final NioEventLoopGroup workerGroup = new NioEventLoopGroup(deviceServerWokerSize);

            final ServerBootstrap bootstrap = new ServerBootstrap();
            try {
                bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, soBlcklog)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) {
                                ch.pipeline().addLast(new IdleStateHandler(60, 300, 300, TimeUnit.SECONDS));
                                ch.pipeline().addLast(new ServerDecoder(new Character[]{(char) 0xAA01, (char) 0xBB01, (char) 0xCC01}));
//                                AA 01 11 00 00 01 01 03 00 10 00 02 C5 CE 72 8D ED
//                                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, Integer.MAX_VALUE, 2, 2, -4, 0, true));
                                ch.pipeline().addLast(new ProtoEncoder());
                                ch.pipeline().addLast(new ProtoDecoder());
                                ch.pipeline().addLast(new ServerHandler());
                                ch.pipeline().addLast(new TimeOutHandler());
                            }
                        });
                log.info("deviceServer is ready ......");
                final ChannelFuture channelFuture = bootstrap.bind(port).sync();
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        });
    }

//    public void send(String deviceCode) {
//        ChannelHandlerContext ctx = DeviceDataContainer.getChannelHandlerContext(deviceCode);
//        if (null != ctx) {
//            ByteBuf byteBuf = ctx.alloc().buffer();
////            AA 01 0D 00  FE 06 00 00 00 01 5 C 05 ED
//            byteBuf.writeByte(0xAA);
//            byteBuf.writeByte(0x01);
//            byteBuf.writeByte(0x0D);
//            byteBuf.writeByte(0x00);
//            byteBuf.writeByte(0xFE);
//            byteBuf.writeByte(0x06);
//            byteBuf.writeByte(0x00);
//            byteBuf.writeByte(0x00);
//            byteBuf.writeByte(0x00);
//            byteBuf.writeByte(0x01);
//            byteBuf.writeByte(0x5C);
//            byteBuf.writeByte(0x05);
//            byteBuf.writeByte(0xED);
//
//            ctx.writeAndFlush(byteBuf);
//        }
//    }

    public void send(int deviceCode, TaskMessage message) {
        ChannelHandlerContext ctx = DeviceDataContainer.getChannelHandlerContext(deviceCode);
        if (null != ctx) {
            log.info("send msg:{}", JSON.toJSONString(message));
            ctx.writeAndFlush(message);
        }
    }

    public void send(ChannelHandlerContext ctx, TaskMessage message) {
        if (null != ctx) {
            log.info("send msg:{}", JSON.toJSONString(message));
            ctx.writeAndFlush(message);
        }
    }

    /**
     * 16进制转换成为string类型字符串
     *
     * @param s
     * @return
     */
    public String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

}
